(function(){
    var crypto = require('crypto');
    var config = require('./config.js');

    function ShortURL(req,res){
        var s = this;
        s.req = req;
        s.res = res;

    }

    ShortURL.prototype = {
        req:null,
        res:null,
        db:null,
        collection:null,
        base64:['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h','i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't','u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H','I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T','U', 'V', 'W', 'X', 'Y', 'Z'],
        open: function(callback){
            var s = this;
            var mongodb = require('mongodb');
            s.db = new mongodb.Db(config.mongodb.name, new mongodb.Server(config.mongodb.host, config.mongodb.port),{safe:false});
            s.db.open(function(err,result){
                if(err){
                    callback.call(s, err);
                }else{
                    s.db.authenticate(config.mongodb.user, config.mongodb.pwd, function(err,result){
                        if(err){
                            callback.call(s, err);
                        } else{
                            s.db.collection('668_1418097304',function(err,result){
                                if(err){
                                    callback.call(s, err);
                                }else{
                                    s.collection = result;
                                    s.isFunction(callback) && callback.call(s, result);
                                }
                            });
                        }
                    });
                }
            });
        },
        getURL: function(key, callback){
            var s = this;
            if(!s.isFunction(callback))return;
            function findOne(collection){
                collection.find({key:key},function(err, result){
                    result.toArray(function(err,result){
                        if(err){callback.call(s, null);throw err;}
                        if(s.isArray(result) && result.length){
                            callback.call(s, result[0]);
                        }else{
                            callback.call(s, null);
                        }
                    });
                });
            }
            if(!s.collection){
                s.open(findOne)
            }else{
                findOne(s.collection);
            }
        },
        getCollection: function(callback){
            if(!this.isFunction(callback))return false;
            if(!this.collection){
                this.open(callback)
            }else{
                callback.call(this,this.collection);
            }
            return true;
        },
        add: function(data, callback){
            if(!this.isFunction(callback))return;
            var self = this;
            this.getCollection(function(collection){
                if(collection){
                    self.getUrlKey(data.url, function(key, exists){
                        if(!key)return;
                        if(!exists){
                            data.key = key;
                            data.userId = data.userId || 0;
                            data.url = data.url || '';
                            data.addTime = data.addTime || new Date();
                            collection.insert(data, {safe: true},function(err,result){
                                if(self.isArray(result) && result.length){
                                    result = result[0].key;
                                }
                                callback.call(collection, err, result);
                            });
                        }else{
                            callback.call(collection, null, key);
                        }
                    });
                }else{
                    callback.call(collection, new Error('collection empty!'), null);
                }
            });
        },
        'delete': function(key, callback){
            if(!this.isFunction(callback))return;
            this.getCollection(function(collection){
               if(collection){
                   collection.remove({key:key},function(err, result){
                       callback.call(collection, err, result);
                   });
               } else{
                   callback.call(collection, new Error('collection empty!'), null);
               }
            });
        },
        'set': function(data, callback){
            if(!this.isFunction(callback))return;
            this.getCollection(function(collection){
                if(collection){
                    var r = collection.update({key:data.key},{$set:data.data});
                    callback.call(collection, r);
                }else{
                    callback.call(collection, false);
                }
            });
        },
        getUrlKey: function(url, callback, i){
            if(!this.isFunction(callback))return;
            var self = this;
            i = i || 0;
            var d = [1,2,3,0];
            var keys = this.GenerateKey(url);
            if(!keys[d[i]])return;
            this.getCollection(function(urls){
                if(urls){
                    urls.find({key:keys[d[i]]}).toArray(function(err, data){
                        if(!isEmptyObject(data)){
                            if(self.isArray(data) && data[0].url === url){
                                callback(keys[d[i]], true);
                            }else{
                                i++;
                                self.getUrlKey(url,callback,i);
                            }
                        }else{
                            callback(keys[d[i]], false);
                        }
                    });
                }
            });
        },
        GenerateKey: function(url){
            var $this = this;
            var md5 = crypto.createHash('md5');
            md5.update('8eoo.com' + url + 'wap5t.com')
            var hex = md5.digest('hex');
            var hexLength = hex.length;
            var subHexLen = hexLength / 8;
            var output = [];
            for(var i=0;i<subHexLen;i++){
                var subHex = hex.substr(i*8,8);
                var int = 0x3FFFFFFF & (('0x' + subHex));
                var out = [];
                for (var j = 0; j < 6; j++){
                    var val = 0x0000003D & int;
                    out.push($this.base64[val ^ 32]);
                    int = int >> 5;
                }
                output.push(out.join(''));
            }
            return output;
        }
    };

    function isEmptyObject(a){for(var b in a)return!1;return!0}

    function each(obj, iterator, context) {
        if (obj == null) return;
        if (obj.length === +obj.length) {
            for (var i = 0, l = obj.length; i < l; i++) {
                if(iterator.call(context, obj[i], i, obj) === false)
                    return false;
            }
        } else {
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if(iterator.call(context, obj[key], key, obj) === false)
                        return false;
                }
            }
        }
    }

    var f = 'Array,Function,String,Number'.split(',');
    each(f, function(v,i){
        ShortURL.prototype['is' + v] = function(obj){
            return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
        }
    });

    module.exports = ShortURL;
})();
